package net.sureon.job.reminder;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import net.sureon.app.AppContext;
import net.sureon.app.UserSession;
import net.sureon.biz.CompanyBO;
import net.sureon.biz.CompanyConfigBO;
import net.sureon.biz.CompanyMgr;
import net.sureon.biz.TitaonMailHelper;
import net.sureon.biz.UserMgr;
import net.sureon.common.exception.SureonException;
import net.sureon.common.mail.MailData;
import net.sureon.common.mail.MailProcessor;
import net.sureon.dao.BalanceOperation;
import net.sureon.dao.ConsumeRecord;
import net.sureon.dao.RealTimeConsume;
import net.sureon.job.SimpleJob;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class BillingJob implements SimpleJob {
	private static final Log logger = LogFactory
	.getLog(BillingJob.class);
	private UserMgr userMgr;
	private CompanyMgr companyMgr;
	/**
	 * @return the userMgr
	 */
	public UserMgr getUserMgr() {
		return userMgr;
	}

	/**
	 * @param userMgr the userMgr to set
	 */
	public void setUserMgr(UserMgr userMgr) {
		this.userMgr = userMgr;
	}

	/**
	 * @return the companyMgr
	 */
	public CompanyMgr getCompanyMgr() {
		return companyMgr;
	}

	/**
	 * @param companyMgr the companyMgr to set
	 */
	public void setCompanyMgr(CompanyMgr companyMgr) {
		this.companyMgr = companyMgr;
	}

	public void execute() throws Exception {
		List<CompanyBO> companies = companyMgr.getAllCompanies();
		long companyID;
		CompanyConfigBO cConfig;
		for (CompanyBO company : companies) {
			UserSession sess = new UserSession(company);
			AppContext.setCurrentUserSession(sess);
			billOneCompany(company);
		}
	}
	
	public static final SimpleDateFormat dataStrInMonth = new SimpleDateFormat("yyyy-MM");
	private void billOneCompany(CompanyBO company) throws SureonException{
		Calendar runTo = Calendar.getInstance();
		runTo.roll(Calendar.MONTH, false);
		runTo.set(Calendar.DAY_OF_MONTH, 1);
		runTo.set(Calendar.HOUR_OF_DAY, 0);
		runTo.set(Calendar.MINUTE, 0);
		runTo.set(Calendar.SECOND, 0);
		Date latestConsume = companyMgr.getLastestConsume(company.getCompanyID());
		if(latestConsume == null){
			/*
			 * first time bill
			 */
			billOneMonth(company, runTo.getTime());
		}else if(dataStrInMonth.format(latestConsume).equals(dataStrInMonth.format(runTo.getTime()))){
			//means already billed
			return;
		}else{
			Calendar consume = Calendar.getInstance();
			consume.setTime(latestConsume);	
			consume.roll(Calendar.MONTH, true);
			consume.set(Calendar.DATE, 1);
			consume.set(Calendar.HOUR_OF_DAY, 0);
			consume.set(Calendar.MINUTE, 0);
			consume.set(Calendar.SECOND, 0);
			boolean find = false;
			while(!consume.after(runTo)){
				find = true;
				billOneMonth(company, consume.getTime());
				consume.roll(Calendar.MONTH, true);
			}
			if(!find){
				logger.warn("no data processed, companyID=" + company.getCompanyID() + ", lastConsume=" + latestConsume);
			}
		}
	}
	
	private void billOneMonth(CompanyBO company, Date theMonth) throws SureonException{
		long companyID = company.getCompanyID();
		RealTimeConsume rtConsume = companyMgr.getCompanyConsumeStatus(companyID, theMonth);
		ConsumeRecord cr = new ConsumeRecord();
		cr.setTotalUserNumber(rtConsume.getTotalUserNumber());
		cr.setFrozeUserNumber(rtConsume.getFrozeUserNumber());
		cr.setNewUserNumber(rtConsume.getNewUserNumber());
		cr.setTotalUserNumber(rtConsume.getTotalUserNumber());
		cr.setBillUserNumber(cr.getTotalUserNumber() - cr.getFrozeUserNumber());
		cr.setCompanyID(company.getCompanyID());
		cr.setConsumeMonth(theMonth);
		cr.setPrice(10);
		cr.setTotalAmount(cr.getPrice() * cr.getBillUserNumber());
		cr.setDiscount(0);
		
		float latestAmount = companyMgr.getCompanyBalance(companyID);
		
		BalanceOperation bo = new BalanceOperation();
		bo.setAmount(cr.getTotalAmount());
		bo.setBalance(latestAmount - bo.getAmount());
		bo.setComments(String.format("billing program auto billing, amount: %1$s,  month: %2$s", cr.getTotalAmount(), theMonth));
		bo.setOperationType(BalanceOperation.OPR_TYPE_CONSUME);
		bo.setCompanyID(companyID);
		bo.setOperationDate(new Date());
		
		companyMgr.insertConsumeRecord(cr);
		companyMgr.insertBalanceOperation(bo);
		
		Map map = TitaonMailHelper.getInstance().generateCompanyMacro(company);
		map.put(TitaonMailHelper.MACRO_REAL_NAME, company.getCompanyVO().getAdminName());
		map.put(TitaonMailHelper.MACRO_TOTALUSERNUMBER, cr.getTotalUserNumber());
		map.put(TitaonMailHelper.MACRO_FROZEUSERNUMBER, cr.getFrozeUserNumber());
		map.put(TitaonMailHelper.MACRO_NEWUSERNUMBER, cr.getNewUserNumber());
		map.put(TitaonMailHelper.MACRO_BILLUSERNUMBER, cr.getBillUserNumber());
		map.put(TitaonMailHelper.MACRO_PRICE, cr.getPrice());
		map.put(TitaonMailHelper.MACRO_TOTALAMOUNT, cr.getTotalAmount());
		map.put(TitaonMailHelper.MACRO_LATESTAMOUNT, latestAmount);
		map.put(TitaonMailHelper.MACRO_BALANCE, bo.getBalance());
		
		map.put(TitaonMailHelper.MACRO_BILLINGDATE, TitaonMailHelper.reminderDateFormat.format(bo.getOperationDate()));
		Calendar cal = Calendar.getInstance();
		cal.setTime(theMonth);
		map.put(TitaonMailHelper.MACRO_YEAR, cal.get(Calendar.YEAR));
		map.put(TitaonMailHelper.MACRO_MONTH, cal.get(Calendar.MONTH));
		MailData md = MailData.defaulMailData();
		md.setReplacementMap(map);
		String[] recipents = null;
		String cusumeEmails = companyMgr.getCompanyConfig(companyID).getConsumeStatisticsEmailAddrs();
		if(cusumeEmails != null && cusumeEmails.length() > 10){
			recipents = cusumeEmails.split(",");
		}else{
			recipents = new String[]{company.getCompanyVO().getAdminEmail()};
		}
		md.setRecipients(recipents);
		String mailName = null;
		if(bo.getBalance() < 0){
			mailName = TitaonMailHelper.MAIL_NAME_BILLING_REMINDER;
		}else{
			mailName = TitaonMailHelper.MAIL_NAME_CONSUME_RECORD;
		}
		try {
			MailProcessor.getInstance().processMail(mailName, md, mailName, AppContext.getLocale());
		} catch (SureonException e) {
			logger.error("process mail failed, mailName:" + mailName + ", companyID:" + companyID);
		}
	}
}
